/**
 *  Copyright (C) 2008-2013  Telosys project org. ( http://www.telosys.org/ )
 *
 *  Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *          http://www.gnu.org/licenses/lgpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.telosys.persistence.database;

import java.io.InputStream;
import java.sql.Connection;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import org.telosys.common.TelosysConfigVar;
import org.telosys.common.TelosysException;
import org.telosys.common.TelosysRuntimeException;
import org.telosys.common.config.TelosysConfigHolder;
import org.telosys.common.logger.TelosysClassLogger;
import org.telosys.persistence.TelosysPersistenceException;
import org.telosys.persistence.connection.ConnectionProvider;
import org.telosys.persistence.connection.ConnectionProviderManager;
import org.telosys.persistence.connection.PoolStatus;
import org.telosys.util.StrUtil;
import org.telosys.util.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * Unique (static) access point to get databases connections or databases sessions <br>
 * This class manages 1 to N databases connection providers
 * 
 * 
 * @author Laurent GUERIN
 *  
 */
public final class DatabaseManager
{
    private static final TelosysClassLogger log               = new TelosysClassLogger(DatabaseManager.class);

    private static final String             RECORDCLASS       = "recordclass" ;

    private static final String             MAPPING_CLASS     = "mappingClass" ; // v 2.0
    
//    private static ConnectionProvider[]     $conProviders     = null;
//
//    private static DatabaseConfig[]         $databases        = null;

    private static Database[]               $databases = null;
    
    private static int                      $iDefaultDatabase = 0;

    //---------------------------------------------------------------------------
    private static void fatalError( String message )
    {
        log.error( message );
        throw new TelosysRuntimeException(message);
    }
    //---------------------------------------------------------------------------
    private static void fatalError( String message, Throwable exception )
    {
        log.error( message );
        log.error( "  Cause : " + exception.getMessage() );
        throw new TelosysRuntimeException(message, exception);
    }
    
    //---------------------------------------------------------------------------
    /**
     * Try to get an element attribute as is or in lower case if not found as is
     * @param element
     * @param attributeName
     * @return
     * @since v 1.0.1
     */
    private static String getAttribute(Element element, String attributeName )
    {
        log.trace("getAttribute(..,'" + attributeName + "') ... ");
        if ( element == null || attributeName == null ) 
        {
            log.error("getAttribute(.,.) null parameter");
            return null ;
        }
        String name = attributeName ;
        log.trace("getAttribute(..,'" + name + "') ... ");
        //--- Try to find it as is ( eg : "defaultId" )
        String s = element.getAttribute(name); // NB : return '' if not found 
        if ( notVoid(s) )
        {
            log.trace("getAttribute(..,'" + name + "') --> '" + s + "'");
            return s ; // Found as is
        }
        else
        {
            //--- Try to find it in lower case ( eg : "defaultid" )
            name = name.toLowerCase();
            s = element.getAttribute(name);
            log.trace("getAttribute(..,'" + name + "') --> '" + s + "'");
            return s ;
        }
    }
    //---------------------------------------------------------------------------
    private static Properties getProperties(Element elem)
    {
        NodeList nodes = elem.getElementsByTagName("property");
        Properties prop = null;
        if (nodes.getLength() > 0)
        {
            prop = new Properties();
            for ( int iProp = 0 ; iProp < nodes.getLength() ; iProp++ )
            {
                Element eltProperty = (Element) nodes.item(iProp);
                String sPropName = eltProperty.getAttribute("name");
                String sPropValue = eltProperty.getAttribute("value");
                prop.put(sPropName, sPropValue);
                log.trace("     " + iProp + " -> property : " + sPropName + "=" + sPropValue);
            }
        }
        return prop;
    }

    //---------------------------------------------------------------------------------------------
    /**
     * Databases environment initialization <br>
     * XML configuration file parsing and creation of the databases environments 
     * 
     * @param sFileName the XML file name
     * @return
     */
    public static boolean init(String sFileName)
    {
        log.info("init(" + sFileName + ")");        
        if (sFileName == null)
        {
//        	String message = "init(" + sFileName + ") file name parameter is null" ;
//            log.error( message );
//            //return false ;
//            throw new TelosysRuntimeException(message);
            fatalError("init(" + sFileName + ") file name parameter is null") ;
        }
        
        Document document = null;
        try
        {
            document = XmlUtil.parse(sFileName);
        } catch (Exception e)
        {
//        	String message = "init(" + sFileName + ") cannot parse XML file" ;
//            log.error(message );
//            //return false ;
//            throw new TelosysRuntimeException(message, e);
            fatalError("init(" + sFileName + ") cannot parse XML file", e) ;
        }
        
        return init( document );
    }
    
    //---------------------------------------------------------------------------------------------
    /**
     * Databases environment initialization <br>
     * XML configuration file parsing and creation of the databases environments 
     * 
     * @param is input stream of the XML configuration file
     * @return
     */
    public static boolean init(InputStream is)
    {
        log.info("init(InputStream)");        
        if (is == null)
        {
//            log.error("init(InputStream) InputStream parameter is null" );
//            return false ;
            fatalError("init(InputStream) InputStream parameter is null") ;
        }
        //--- Keep the dbconfig file name
        // $sConfigFileName = null ; // v 1.1.1
        
        //Document document = XmlUtil.loadFile(sFileName);
        Document document = null;
        try
        {
            document = XmlUtil.parse(is);
        } catch (Exception e)
        {
//            log.error("init(InputStream) cannot parse. TelosysException : " + e.getMessage() );
//            return false ;
            fatalError("init(InputStream) cannot parse XML file", e) ;
        }
        
        return init( document );
    }
    
    //---------------------------------------------------------------------------------------------
    private static boolean notVoid( String s  ) // v 1.0.0
    {
        if ( s != null )
        {
            if ( s.trim().length() > 0 )
            {
                return true ;
            }
        }
        return false ;
    }
    
    //---------------------------------------------------------------------------------------------
    /**
     * Try to get a class name pattern from "dbconfig.xml" and "telosys.properties"
     * @param sParamName
     * @param root
     * @param db
     * @param dbId
     * @return
     */
    private static String getClassPattern( String sParamName, Element root, Element db, String dbId  ) // v 1.0.0
    {
        //--- 1) Try to find a specific pattern from DATABASE element in XML config "databases.dbcfg"
        String s = db.getAttribute(sParamName);
        if ( notVoid(s) ) 
        {
            return s.trim() ; // Return it "as is"
        }

        //--- 2) Try to find a generic pattern from ROOT element in XML config "databases.dbcfg" 
        s = root.getAttribute(sParamName);
        if ( notVoid(s) && dbId != null )
        {
            return StrUtil.replaceVar(s.trim(), TelosysConfigVar.DBID, dbId.trim() );
        }

        //--- 3) Try to find a generic pattern in "telosys.properties" 
        Properties prop = TelosysConfigHolder.getProperties();
        
        if ( prop != null ) // Can be null if Telosys-Persistence is initialized in standalone mode (without Telosys properties)
        {
            s = prop.getProperty(sParamName);
            if ( notVoid(s) && dbId != null )
            {
                return StrUtil.replaceVar(s.trim(), TelosysConfigVar.DBID, dbId.trim() );
            }
        }
        
        //--- 4) Not found => no pattern for this database
        return null ;
    }
    //---------------------------------------------------------------------------------------------
//    /**
//     * Try to get a DAO registry class name, from the more specific configuration to the more general
//     * @param root
//     * @param db
//     * @param dbId
//     * @return
//     */
//    private static String getDAORegistryClass( Element root, Element db, String dbId  ) // v 1.0.0
//    {
//        return getClassPattern( DAOREGISTRY, root, db, dbId );
//    }
//    
    //---------------------------------------------------------------------------------------------
//    /**
//     * Try to get a DAO class name pattern, from the more specific configuration to the more general
//     * @param root
//     * @param db
//     * @param dbId
//     * @return
//     */
//    private static String getDAOClassPattern( Element root, Element db, String dbId  ) // v 1.0.0
//    {
//        return getClassPattern( DAOCLASS, root, db, dbId );
//    }
    
    private static String getRecordClassPattern( Element root, Element db, String dbId  ) // v 1.0.0
    {
        return getClassPattern( RECORDCLASS, root, db, dbId );
    }
    
    private static String getMappingClassPattern( Element root, Element db, String dbId  ) // v 2.0.0
    {
        return getClassPattern( MAPPING_CLASS, root, db, dbId );
    }

    private static String[] getSqlFolders( String foldersList  ) // v 2.0.0
    {
    	if ( foldersList != null ) {
        	String[] parts = StrUtil.split(foldersList, ',');
        	String[] trimedParts = new String[parts.length];
        	for ( int i=0 ; i < parts.length ; i++ ) {
        		trimedParts[i] = parts[i].trim() ;
        	}
        	return trimedParts ;
    	}
    	else {
    		return null ;
    	}
    }
    
    //---------------------------------------------------------------------------------------------
    private static boolean init( Document document )
    {
        log.trace("init(Document)");        
        if (document == null)
        {
            return false ;
        }
        
        Element root = document.getDocumentElement();

        //--- Number of Databases = Maximum Database ID + 1 ( 10 if not set, cannot exceed 100)
//        String sMaxId = getAttribute(root, "maxId"); // v 1.0.1
//        int iMaxId = StrUtil.getInt(sMaxId, 10);
//        if (iMaxId > 100)
//        {
//            log.error("maxId = " + iMaxId + " : maxId > 100 => reduced to 100 !");
//            iMaxId = 100;
//        }
//        int iNbDatabases = iMaxId + 1;
        //log.trace("Maximum database Id = '" + sMaxId + "' ( int : " + iMaxId + ")");
        //log.trace("Number of database  = " + iNbDatabases + " ( maxId + 1 ) ");

        //--- Default Database ID ( 0 if not set )
        String sDefaultId = getAttribute(root, "defaultId"); // v 1.0.1
        $iDefaultDatabase = StrUtil.getInt(sDefaultId, 0);
//        if ($iDefaultDatabase < 0 || $iDefaultDatabase >= iNbDatabases)
//        {
//            log.error("Invalid default database (" + $iDefaultDatabase + ") : set to 0 ");
//            $iDefaultDatabase = 0;
//        }
        log.trace("Default database Id = " + $iDefaultDatabase );

//        //--- Array of 'N' databases
//        $databases = new DatabaseConfig[iNbDatabases];
        
//        //--- Array of 'N' providers
//        $conProviders = new ConnectionProvider[iNbDatabases];
//        log.info("Ready to store " + iNbDatabases + " connection providers ( ID from 0 to " + iMaxId + ", default = " + $iDefaultDatabase +" )");

        //--- Databases list
        NodeList nodes = document.getElementsByTagName("db");
//        int nbDbNodes = nodes.getLength();

/*****
        //--- For each database defined in the XML file ...
        for ( int i = 0 ; i < nbDbNodes ; i++ )
        {
            //--- Tag "bd" ( get the attributes )
            Element eltBase = (Element) nodes.item(i);
            if (eltBase == null)
            {
                log.error("XML 'db' node is null ( Node " + i + " ) !");
            }
            else
            {
                String sDbId = eltBase.getAttribute("id");
                int iDbId = StrUtil.getInt(sDbId, -1);
                String sDbName = eltBase.getAttribute("name");

                //--- Database managed by a DataSource
                //String sDataSource = eltBase.getAttribute("datasource");
                String sDataSource = getAttribute(eltBase, "dataSource"); // v 1.0.1

                //--- Database managed by a specific custom Class
                String sCustomClass = getAttribute(eltBase, "customClass"); // v 1.0.1
                
                //--- Database managed by Telosys-Persistence
                String sDbDriver = eltBase.getAttribute("driver");
                String sDbUrl = eltBase.getAttribute("url");
                //String sDbIsolationLevel = eltBase.getAttribute("isolationLevel");
                String sDbIsolationLevel = getAttribute(eltBase, "isolationLevel"); // v 1.0.1
                //int iDbIsolationLevel = SqlTool.getIsolationLevel(sDbIsolationLevel, Connection.TRANSACTION_READ_COMMITTED);
                //String sDbPoolSize = eltBase.getAttribute("poolSize");
                String sDbPoolSize = getAttribute(eltBase, "poolSize"); // v 1.0.1
                //int iDbPoolSize = StrUtil.getInt(sDbPoolSize, 0);

                //--- Tags "property"
                Properties properties = getProperties(eltBase);

//                //--- Try to get a DAO Registry class name for this database
//                String sDAORegistryClass = getDAORegistryClass( root, eltBase, sDbId  ); // v 1.0.0
//                //--- Try to get a DAO class name pattern for this database
//                String sDAOClassPattern = getDAOClassPattern( root, eltBase, sDbId  ); // v 1.0.0                
                //--- Try to get a record class name pattern for this database
                String sRecordClassPattern = getRecordClassPattern( root, eltBase, sDbId  ); // v 1.0.0                
                
                //--- Try to get a record class name pattern for this database
                String sMappingClassPattern = getMappingClassPattern( root, eltBase, sDbId  ); // v 2.0.0                
                
                String sqlFoldersList = getAttribute(eltBase, "sqlFolders"); // v 2.0.0
                String[] sqlFolders = getSqlFolders(sqlFoldersList);
                
                //--- Connection Provider creation :
                // v 1.0.1 // ConnectionProvider cp = createConnectionProvider(sDbId, sDbName, sDataSource, sDbDriver, sDbUrl, props, sDbIsolationLevel, sDbPoolSize);
                ConnectionProvider connectionProvider = null ;
                if ( notVoid(sCustomClass) ) // v 1.0.1
                {
                    //connectionProvider = createCustomClassConnectionProvider(sDbId, sDbName, sCustomClass); // v 1.0.1
                    connectionProvider = ConnectionProviderManager.createCustomClassConnectionProvider(sDbId, sDbName, sCustomClass); // v 2.0
                }
                else
                {
                    //connectionProvider = createConnectionProvider(sDbId, sDbName, sDataSource, sDbDriver, sDbUrl, props, sDbIsolationLevel, sDbPoolSize);
                	// v 2.0
                    connectionProvider = ConnectionProviderManager.createConnectionProvider(sDbId, sDbName, sDataSource, sDbDriver, sDbUrl, props, sDbIsolationLevel, sDbPoolSize);
                }
                if ( connectionProvider != null )
                {
                    log.info("DB " + sDbId + " (" + sDbName + ") : ConnectionProvider created.");                        
                }
                
                //--- Store provider in the array
                if (iDbId >= 0 && iDbId < $conProviders.length)
                {
                    if ($conProviders[iDbId] == null)
                    {
                        if (connectionProvider != null)
                        {
                            //--- Register the database connection provider
                            $conProviders[iDbId] = connectionProvider;
                            log.info("DB " + sDbId + " (" + sDbName + ") : ConnectionProvider ready.");
                            
                            //--- Register the database definition
                            int dbPoolSize = StrUtil.getInt(sDbPoolSize, 0);
                            $databases[iDbId] = new DatabaseConfig(iDbId, sDbName, sDbDriver, 
                                    sDbUrl, sDbIsolationLevel, 
                                    //sDbPoolSize, 
                                    dbPoolSize, 
                                    //sDAORegistryClass,
                                    //sDAOClassPattern,
                                    sRecordClassPattern, 
                                    sMappingClassPattern, // v 2.0.0
                                    sqlFolders);  // v 2.0.0                        
                        }
                        else
                        {
                            log.error("DB " + sDbId + " (" + sDbName + ") : ConnectionProvider is NULL !");
                        }
                    }
                    else
                    {
                        log.error("Database ID " + iDbId + " is already used : cannot register this database ! ");
                    }
                }
                else
                {
                    log.error("Invalid Database ID ( " + iDbId + " ) : cannot register this database ! ");
                    throw new TelosysPersistenceException("Invalid database id " + iDbId );
                }
            }
        }
*****/
        
        //--- Get all the databases configurations from the XML file
        List<DatabaseConfig> databasesConfig = getDatabasesConfig(root, nodes) ;

        //--- Determine the max ID in order to size the array of databases 
        int maxId = 0 ;
        for ( DatabaseConfig dbConfig : databasesConfig ) {
        	int dbId = dbConfig.getId() ;
        	if ( dbId > maxId ) {
        		maxId = dbId ;
        	}
        }
        log.info("Database max ID = " + maxId );

        //--- Create the array of databases 
        int size = maxId + 1 ;
        log.info("Preparing database storage ( size = " + size + " )");
        $databases = new Database[maxId + 1] ;
        
        //--- Create a Database instance with a Connection Provider for each configured database and store it
        log.info("Creating databases with connection provider...");
        for ( DatabaseConfig dbConfig : databasesConfig ) {
        	int dbId = dbConfig.getId();
        	ConnectionProvider connectionProvider = getConnectionProvider(dbConfig);
        	Database database = new Database(dbConfig, connectionProvider); 
        	$databases[dbId] = database ; 
            log.info(" . Database #"+ dbId + " ready, connection provider type : " 
            			+ database.getConnectionProvider().getTypeName() );
        }
        
        return true ;
    }

    private static List<DatabaseConfig> getDatabasesConfig(Element root, NodeList nodes) {
    	
    	LinkedList<DatabaseConfig> databases = new LinkedList<DatabaseConfig>();
    	
        //--- For each database defined in the XML file ...
        for ( int i = 0 ;  i < nodes.getLength() ;  i++ )
        {
            Element eltBase = (Element) nodes.item(i);
            if (eltBase == null)
            {
                log.error("XML 'db' node is null ( Node " + i + " ) !");
            }
            else
            {
                String sDbId = eltBase.getAttribute("id");
                int iDbId = StrUtil.getInt(sDbId, -1);
                String sDbName = eltBase.getAttribute("name");

                //--- Database managed by a DataSource
                String sDataSource = getAttribute(eltBase, "dataSource");

                //--- Database managed by a specific custom Class
                String sCustomClass = getAttribute(eltBase, "customClass");
                
                //--- Database managed by a specific custom Class
                String dbFeaturesClassName = getAttribute(eltBase, "dbFeaturesClassName");
                
                //--- Database managed by Telosys-Persistence
                String sDbDriver = eltBase.getAttribute("driver");
                String sDbUrl = eltBase.getAttribute("url");
                String sDbIsolationLevel = getAttribute(eltBase, "isolationLevel");
                String sDbPoolSize = getAttribute(eltBase, "poolSize");
                int dbPoolSize = StrUtil.getInt(sDbPoolSize, 0);

                //--- Tags "property"
                Properties properties = getProperties(eltBase);

//                //--- Try to get a DAO Registry class name for this database
//                String sDAORegistryClass = getDAORegistryClass( root, eltBase, sDbId  ); // v 1.0.0
//                //--- Try to get a DAO class name pattern for this database
//                String sDAOClassPattern = getDAOClassPattern( root, eltBase, sDbId  ); // v 1.0.0                

                //--- Try to get a record class name pattern for this database
                String sRecordClassPattern = getRecordClassPattern( root, eltBase, sDbId  ); // v 1.0.0                
                
                //--- Try to get a record class name pattern for this database
                String sMappingClassPattern = getMappingClassPattern( root, eltBase, sDbId  ); // v 2.0.0                
                
                String sqlFoldersList = getAttribute(eltBase, "sqlFolders"); // v 2.0.0
                String[] sqlFolders = getSqlFolders(sqlFoldersList);
                
                //--- Register the database definition
                DatabaseConfig databaseConfig = new DatabaseConfig(iDbId, sDbName, sDbDriver, 
                        sDbUrl, 
                        sDbIsolationLevel, 
                        
                        dbPoolSize, 
                        sDataSource, // v 2.0.0
                        sCustomClass, // v 2.0.0
                        
                        properties, // v 2.0.0
                        dbFeaturesClassName, // v 2.0.0
                        
                        sRecordClassPattern, 
                        sMappingClassPattern, // v 2.0.0
                        sqlFolders);  // v 2.0.0                        
                
                //--- One more database
                databases.add(databaseConfig);
                
            }
        }
    	return databases ;
    }
    
    private static ConnectionProvider getConnectionProvider(DatabaseConfig databaseConfig) {
    	
//    	String sCustomClass = databaseConfig.getConnectionProviderCustomClass();
    	int    dbId   = databaseConfig.getId();
    	String dbName = databaseConfig.getName();
    	
        //--- Connection Provider creation :
//        // v 1.0.1 // ConnectionProvider cp = createConnectionProvider(sDbId, sDbName, sDataSource, sDbDriver, sDbUrl, props, sDbIsolationLevel, sDbPoolSize);
//        ConnectionProvider connectionProvider = null ;
//        if ( notVoid(sCustomClass) ) // v 1.0.1
//        {
//            //connectionProvider = createCustomClassConnectionProvider(sDbId, sDbName, sCustomClass); // v 1.0.1
//            connectionProvider = ConnectionProviderManager.createCustomClassConnectionProvider(sDbId, dbName, sCustomClass); // v 2.0
//        }
//        else
//        {
//            //connectionProvider = createConnectionProvider(sDbId, sDbName, sDataSource, sDbDriver, sDbUrl, props, sDbIsolationLevel, sDbPoolSize);
//        	// v 2.0
//            //connectionProvider = ConnectionProviderManager.createConnectionProvider(sDbId, dbName, sDataSource, sDbDriver, sDbUrl, props, sDbIsolationLevel, sDbPoolSize);
//            connectionProvider = ConnectionProviderManager.createConnectionProvider(databaseConfig);
//        }
        
        ConnectionProvider connectionProvider = ConnectionProviderManager.createConnectionProvider(databaseConfig);
        if ( connectionProvider != null )
        {
            log.info("DB " + dbId + " (" + dbName + ") : ConnectionProvider created.");                        
        }
    	return connectionProvider ;
    }
    
//    //---------------------------------------------------------------------------------------------
//    /**
//     * Returns a real database session for the default database id.
//     * @return
//     * @throws TelosysException
//     */
//    public static DatabaseSession getSession() throws TelosysException
//    {
//        return getSession($iDefaultDatabase);
//    }

//    //---------------------------------------------------------------------------------------------
//    /**
//     * Returns a real database session for the given database id.
//     * @param iDatabaseId
//     * @return
//     * @throws TelosysException
//     */
//    public static DatabaseSession getSession(int iDatabaseId) throws TelosysException
//    {
//        Connection con = getConnection(iDatabaseId);
//        return new StandardDatabaseSession(iDatabaseId, con);
//    }

//    //---------------------------------------------------------------------------------------------
//    /**
//     * Returns a "dummy" database session.
//     * @return
//     * @since v 1.0.2
//     */
//    public static DatabaseSession getDummySession() 
//    {
//        return $dummyDbSession ;
//    }

    //---------------------------------------------------------------------------------------------
//    /**
//     * Returns the configuration file name used to initialized the Connection Manager
//     * 
//     * @return the file name (or null)
//     */
//    public static String getConfigFileName()
//    {
//        return $sConfigFileName;
//    }

    //---------------------------------------------------------------------------------------------
    /**
     * Returns the default database id
     * 
     * @return
     */
    public static int getDefaultDatabase()
    {
        return $iDefaultDatabase;
    }

    //---------------------------------------------------------------------------------------------
    /**
     * Returns the number of databases entries supported by the connection manager <br>
     * including void entries
     * 
     * @return
     */
    public static int getNumberOfDatabases()
    {
//        if ($conProviders != null)
//        {
//            return $conProviders.length;
//        }
//        return 0;
        if ($databases != null)
        {
            return $databases.length;
        }
        return 0;
    }

    //---------------------------------------------------------------------------------------------
    /**
     * Returns a connection for the default database
     * 
     * @return
     * @throws TelosysException
     */
    public static Connection getConnection() // throws TelosysException
    {
        return getConnection($iDefaultDatabase);
    }

    //---------------------------------------------------------------------------------------------
    /**
     * Returns a connection for the given database id
     * 
     * @param iBaseId
     * @return
     * @throws TelosysException
     */
    public static Connection getConnection(int iBaseId) // throws TelosysException
    {
        //SqlConnectionProvider cp = getConnectionProvider(iBaseId);
        ConnectionProvider cp = getConnectionProvider(iBaseId);
        if (cp != null)
        {
            try
            {
                return cp.getConnection();
            } catch (Exception ex)
            {
                //throw new TelosysException("getConnection(" + iBaseId + ") : Exception ! " + ex);
                throw new TelosysPersistenceException("getConnection(" + iBaseId + ") : Exception ! " + ex);
            }
        }
        else
        {
            //throw new TelosysException("getConnection(" + iBaseId + ") : No ConnectionProvider for this DB id !");
            throw new TelosysPersistenceException("getConnection(" + iBaseId + ") : No ConnectionProvider for this DB id !");
        }
    }

    //---------------------------------------------------------------------------------------------
    /**
     * Returns the default database connection provider
     * 
     * @return
     */
    protected static ConnectionProvider getConnectionProvider()
    {
        return getConnectionProvider($iDefaultDatabase);
    }

    //---------------------------------------------------------------------------------------------
    /**
     * Return the connection provider for the given database id
     * 
     * @param iBaseId
     * @return the connection provider (or null if no provider for the given DB id)
     */
    protected static ConnectionProvider getConnectionProvider(int iBaseId)
    {
    	if ( $databases != null ) {
            if (iBaseId >= 0 && iBaseId <= $databases.length)
            {
            	Database database = $databases[iBaseId] ;
                if ( database != null )
                {
                    return database.getConnectionProvider();
                }
                else
                {
                    log.error("getConnectionProvider(" + iBaseId + ") : unknown database ID ! ");
                    return null;
                }
            }
            else
            {
                log.error("getConnectionProvider(" + iBaseId + ") : invalid database ID ! ");
                return null;
            }
    	}
        else
        {
            log.error("getConnectionProvider(" + iBaseId + ") : databases not initialized ! ");
            return null;
        }
//        if ($conProviders != null)
//        {
//            if (iBaseId >= 0 && iBaseId <= $conProviders.length)
//            {
//                if ($conProviders[iBaseId] != null)
//                {
//                    return $conProviders[iBaseId];
//                }
//                else
//                {
//                    log.error("getConnectionProvider(" + iBaseId + ") : no connection provider for this database ID ! ");
//                    return null;
//                }
//            }
//            else
//            {
//                log.error("getConnectionProvider(" + iBaseId + ") : invalid database ID ! ");
//                return null;
//            }
//        }
//        else
//        {
//            log.error("getConnectionProvider(" + iBaseId + ") : connection providers not initialized ! ");
//            return null;
//        }
    }
    //---------------------------------------------------------------------------------------------
    /**
     * Returns the list of all the databases managed by the current application  
     * @return list of databases descriptions 
     */
    //public static List getDatabases()
//    public static List<DatabaseConfig> getDatabases()
//    {
//        LinkedList<DatabaseConfig> list = new LinkedList<DatabaseConfig>();
//        if ( $databases != null )
//        {
//            DatabaseConfig db = null ;
//            int len = $databases.length ;
//            for ( int i = 0 ; i < len ; i++ )
//            {
//                db = $databases[i];
//                if ( db != null )
//                {
//                    list.add(db);
//                }
//            }
//        }
//        return list ;
//    }
    public static List<Database> getDatabases()
    {
        LinkedList<Database> list = new LinkedList<Database>();
        if ( $databases != null )
        {
            int len = $databases.length ;
            for ( int i = 0 ; i < len ; i++ )
            {
            	Database db = $databases[i];
                if ( db != null )
                {
                    list.add(db);
                }
            }
        }
        return list ;
    }
    //---------------------------------------------------------------------------------------------
    /**
     * Returns the database description for the given database id
     * @param id the database id ( 0 to N )
     * @return the database description or null 
     * @since 1.0.0
     */
//    public static DatabaseConfig getDatabase(int id)
//    {
//        if ( id >= 0 && id < $databases.length )
//        {
//            return $databases[id];
//        }
//        return null ;
//    }
    public static Database getDatabase(int id)
    {
        if ( id >= 0 && id < $databases.length )
        {
            return $databases[id];
        }
        return null ;
    }
    
    public static DatabaseInfo getDatabaseInfo(int id)
    {
    	Database database = getDatabase(id);
    	if ( database != null ) {
            return new DatabaseInfo(database);
    	}
    	else {
    		throw new TelosysPersistenceException("getDatabaseInfo(" + id + ") : No database with this id.");
    	}
    }
    //---------------------------------------------------------------------------------------------
    /**
     * Returns the pool size for the given database id <br>
     * or 0 if the database exists but doesn't use a Telosys pool ( ConnectionFactory or DataSource )<br>
     * or -1 if there's no database for the given id
     * @param id the database id ( 0 to N )
     * @return the size of the pool ( or 0 or -1  ) 
     * @since 1.0.0
     */
    //public static int getPoolSize(int id)
    /**
     * Returns the status of the connection pool for the given database id<br>
     * or null if there's no database for the given id
     * @param id
     * @return
     */
    public static PoolStatus getPoolStatus(int id)
    {
        ConnectionProvider cp = getConnectionProvider(id);
        
        if ( cp != null )
        {
//            if ( cp instanceof ConnectionProviderPool )
//            {
//                return ((ConnectionProviderPool)cp).getCurrentSize();
//            }
//            else
//            {
//                return 0 ;
//            }
            //return cp.getCurrentPoolSize(); // v 2.0
            return cp.getPoolStatus(); // v 2.0
        }
        return null;
    }
    
    /**
     * Returns the ConnectionProvider's class for the given database id. 
     * @param id the database id ( 0 to N )
     * @return the class or null if there's no database for the given id
     * @since 1.0.0
     */
    //public static Class getProviderClass(int id)
    public static Class<? extends ConnectionProvider> getConnectionProviderClass(int id)
    {
        ConnectionProvider cp = getConnectionProvider(id);
        if ( cp != null )
        {
            return cp.getClass();
        }
        return null ;
    }

    /**
     * Returns the type of ConnectionProvider <br>
     * one of the ConnectionProvider types : FACTORY, POOL, DATASOURCE <br>
     * or NO_PROVIDER if there's no database for the given id 
     * @return
     * @since 1.0.0
     */
    public static int getConnectionProviderType(int id)
    {
        ConnectionProvider cp = getConnectionProvider(id);
        if ( cp != null )
        {
            return cp.getType();
        }
        return ConnectionProvider.NO_PROVIDER ;
    }

}